Skip to content

Render provenance and citation markers as grouped icon asides - #180

Merged
simonpcouch merged 7 commits into
mainfrom
feat/aside-icon-markers
Aug 27, 2026
Merged

Render provenance and citation markers as grouped icon asides#180
simonpcouch merged 7 commits into
mainfrom
feat/aside-icon-markers

Conversation

@cpsievert

@cpsievert cpsievert commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Supersedes #166. Closes #148 (default shinychat citation UI) and closes
#149 (minimal markers) — the look #149 asked for, through the mechanism
#148 asked for.

Where things stand

Both PRs chase the same destination: small icon markers instead of
pills, with a one-shot pulse on mount. #166 gets there with commons-owned
CSS that restyles shinychat's compact-aside DOM state by state; this PR
gets there by changing what commons asks shinychat to render — labeled
identity asides — and confining commons CSS to shinychat's public
--shiny-chat-aside-marker-* theming variables.

How they compare, at a glance:

#166 This PR
Marker look Small icon, pulse on mount Same look (warning icon identical to #166's; citation quote mark redrawn as a fixed path)
Mechanism CSS overrides of shinychat's compact-aside DOM shinychat's aside grouping + public theme variables
Multiple citations in one paragraph Flattened to one dot, indistinguishable from a single citation One marker with a "+N" overflow count and a carousel popover; every citation stays reachable
Hover/focus/expanded states Re-implemented per tone in commons CSS Inherited from shinychat
Cited answers in review transcript Marker removed Unchanged here; see note below

What this PR does

Conceptually, three moves:

  • Citations render as labeled identity asides instead of compact
    numbered chips, so shinychat owns the marker entirely — grouping,
    overflow counts, popovers, and interaction states.
  • All three markers (Verified, Untrusted, citation) render
    icon-only.
    The label stays in the DOM for the pill's aria-label,
    and commons CSS touches only shinychat's public
    --shiny-chat-aside-marker-* variables.
  • Singleton citation popovers gain a body title (icon + source
    label), since shinychat only renders its own title row for grouped
    asides.

A local example app renders every outcome — including a three-citation
paragraph and a singleton citation — for manual comparison against the
review transcript. It's not part of the package; source below.

Example app: all provenance outcomes and grouped citations
# Save as app.R and run with shiny::runApp().
#
# What a live commons chat renders for each provenance outcome:
# Verified and Untrusted answers end with a marker; Cited answers show
# their citation asides instead — including a paragraph with several
# citations, which shinychat accumulates into one marker with a "+N"
# overflow and a carousel popover. A fourth message carries a lone
# citation: shinychat only renders its popover title row for grouped
# asides, so commons puts its own title (icon + source label) at the
# top of the aside body to keep singleton popovers named too.

corpus <- list(
  list(
    label = "documentation",
    kind = "prose",
    text = "Revenue excludes tax and shipping."
  ),
  list(
    label = "sales table",
    kind = "schema",
    text = "Revenue is recognized at shipment, not at order placement."
  ),
  list(
    label = "measure definitions",
    kind = "definition",
    text = "Gross margin is net revenue minus cost of goods sold."
  )
)

citation <- function(quote, explanation) {
  render_citation_aside(quote, explanation, corpus)$html
}

ui <- bslib::page_fillable(
  theme = commons_theme(),
  shinychat::chat_ui(
    "chat",
    messages = list(
      list(
        role = "assistant",
        content = paste(
          "6 orders came in last week.",
          provenance_aside("A")
        )
      ),
      list(
        role = "assistant",
        content = paste(
          "Revenue excludes tax and shipping, is recognized at shipment,",
          "and feeds straight into gross margin.",
          citation(
            "Revenue excludes tax and shipping.",
            "Supports the definition given in the answer."
          ),
          citation(
            "Revenue is recognized at shipment, not at order placement.",
            "Supports the recognition timing."
          ),
          citation(
            "Gross margin is net revenue minus cost of goods sold.",
            "Supports the margin description."
          )
        )
      ),
      # A lone citation: shinychat only renders its popover title row
      # for grouped asides, so the aside body carries its own title
      # (icon + source label) to name the source here too.
      list(
        role = "assistant",
        content = paste(
          "Revenue is recognized at shipment, not at order placement.",
          citation(
            "Revenue is recognized at shipment, not at order placement.",
            "Supports the recognition timing."
          )
        )
      ),
      list(
        role = "assistant",
        content = paste(
          "Margins appear roughly flat. Treat this as an estimate.",
          provenance_aside("C")
        )
      )
    )
  )
)

server <- function(input, output, session) {}

shiny::shinyApp(ui, server)
Screenshot 2026-08-27 at 12 35 35 PM

Notes

  • Icon concepts are transition from pills to badges #166's, with glyphs from Radix Icons (MIT).
    The warning mark is a white exclamation on the original yellow
    circle, matching transition from pills to badges #166. The citation mark is a "quote" glyph in
    white on a navy tile — transition from pills to badges #166's serif " text element was
    white-on-transparent, designed to sit on a solid dot, and its
    metrics shifted with font substitution; the redraw is a fixed
    <path>. The trusted mark is a "check" glyph in white on the
    green shield badge.
  • Citations deliberately don't pulse. Verified and Untrusted keep
    transition from pills to badges #166's one-shot pulse (disabled under prefers-reduced-motion); the
    pulse should say "check this answer's provenance," not "this answer
    cites something."
  • Cited answers keep their review marker here; transition from pills to badges #166 removes it.
    Live chat shows no Cited marker in either PR — the citation asides
    are the signal. But review transcripts don't recreate inline
    citations today, so removing the marker there would leave Cited
    answers indistinguishable from answers with no data activity. Worth
    revisiting once the (forthcoming) sibling replay PR lands citation
    asides in review.

Validation

Unit tests updated and passing (test-citations.R, test-provenance.R).
Browser tests updated for the new markup and passing
(test-citation-browser.R, 27 expectations against live chromote).
Manual check of the
example app above recommended for glyph placement, the stadium shape on
grouped markers, pulse timing, and dark mode (tone hexes were chosen
for light backgrounds).

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

Preview deployed to Connect (dogfood.team.pct.posit.it): https://dogfood.team.pct.posit.it/connect/#/apps/d7a36cae-8f27-448b-a478-61b81fbe3942/draft/365373

Deployed from commit c8d1ded.

@cpsievert

Copy link
Copy Markdown
Collaborator Author

@simonpcouch I feel like the icons could still use some polish, but wanted to get this in front of you sooner rather than later. Let me know if anything else jumps out to you.

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

Preview deployed to Connect (connect.staging.pct.posit.it): https://connect.staging.pct.posit.it/connect/#/apps/ad662e1b-5048-4acc-9ad7-f9478c92274e/draft/2301

Deployed from commit c8d1ded.

@cpsievert
cpsievert force-pushed the feat/aside-icon-markers branch from 95d25b2 to 7cc158e Compare August 27, 2026 17:41
An alternative to #166 built on shinychat's aside rendering; closes #148,
closes #149.

- Citations drop display="compact" and render as labeled identity
  asides: shinychat accumulates same-paragraph citations into one marker
  with a +N overflow and a carousel popover, instead of commons styling
  numbered markers itself.
- Verified/Untrusted/citation markers render as bare icons (the label
  stays in the DOM for aria), with tone supplied only through shinychat's
  public --shiny-chat-aside-marker-* variables, so its own hover, focus,
  and expanded states keep working. Grouped markers get a stadium shape;
  bare icons stay circular.
- Adopts #166's artwork: the warning icon's white glyph on a caution
  triangle (the familiar-signs idea from #149), and its quote-mark
  citation glyph (recolored slate, reframed for bare rendering). The
  aside's icon attribute carries the per-kind icon, so the popover title
  row shows it; the pill paints the uniform quote mark over it via CSS
  (a marked TRIAL block, deleted if shinychat gains a popover-icon
  attribute).
- Provenance markers keep a one-shot pulse, disabled under
  prefers-reduced-motion.
shinychat only renders the popover's title row (icon + label) for
grouped asides, so a lone citation opened a popover with no visible
source label — the pill label it treats as redundant is hidden by
commons's icon-only styling. The aside body now carries its own title
(per-kind icon + label), and commons-chat.css hides shinychat's title
row in those popovers so grouped asides don't name the source twice.

The example app gains a singleton-citation message covering the case;
the browser test's label/icon expectations pass unmodified again.
With the source named by the aside body's own title, the popover no
longer uses the aside's icon attribute — so it can carry the uniform
citation-mark.svg directly. The pill renders it like any other marker
icon, deleting the TRIAL block that hid the per-kind img and repainted
the quote mark via ::before, and tightening the bridge selectors from
a citation-* prefix match to a single suffix. The repeated :has()
selector lists consolidate under :is(). Same rendered result; the
browser suite passes unmodified.
The bare slate quote glyph read as punctuation inline and broke the
family's filled-sign-plus-white-glyph convention (shield, triangle).
The citation marker is now Bootstrap Icons' quote glyph (MIT) in
white on a mid-navy (#2e4a6b) rounded-square tile; the Untrusted
triangle likewise adopts exclamation-triangle-fill, padded to a
common viewBox. Plain paths throughout — no runtime font dependency,
unlike #166's <text>-based citation mark. The Verified shield keeps
its existing artwork. Pill tones move from slate to matching navy.
Also drops the orphaned untrusted-icon.svg.
Drops this branch's redrawn caution triangle in favor of the original
yellow circle with the white exclamation glyph, matching what #166
proposes, so the two PRs differ only in mechanism, not artwork.
Unify the aside marker icons on one MIT-licensed set: quote on the
navy citation tile, check on the trusted shield, exclamation on the
warning disc, and file-text/table/ruler-horizontal for the per-kind
citation icons. Fills and strokes are baked in since the markers
render as <img> and cannot inherit currentColor. Pill markers bump
from 1.35em to 1.5em.
@simonpcouch

Copy link
Copy Markdown
Collaborator

How this feels currently:

Screen.Recording.2026-08-27.at.10.47.05.AM.mov

@cpsievert
cpsievert force-pushed the feat/aside-icon-markers branch from 7cc158e to a8c6f6b Compare August 27, 2026 17:49
@cpsievert

Copy link
Copy Markdown
Collaborator Author

Oh, sorry I ended up having to do a force push, but I think really the only real change since you've looked at this is probably that I've moved some of the icons from Bootstrap to Radix UI (both MIT licensed).

@cpsievert
cpsievert marked this pull request as ready for review August 27, 2026 17:52
@simonpcouch

Copy link
Copy Markdown
Collaborator

Oh, sorry I ended up having to do a force push, but I think really the only real change since you've looked at this is probably that I've moved some of the icons from Bootstrap to Radix UI (both MIT licensed).

No worries, I was just getting a sense for the UIUX :)

@simonpcouch simonpcouch left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome, I'm really appreciative of you making this happen. Let's bring this in, after that one icon change!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only thought when using this was the proportion of whitespace inside of this icon is a good bit higher than the other two icons. Could we shrink the quotation marks slightly?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a slightly lighter blue, as well? It's giving Merriam-Webster🤪

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shrunk the glyph to match the others' proportions in c8d1ded.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lightened to #55729e in c8d1ded — also softened the warning yellow while I was at it.

Shrink the citation quotation glyph (0.85 -> 0.70, centered like the
sibling glyphs) so its interior whitespace matches the other markers,
lighten the tile from navy #2e4a6b to #55729e, and soften the warning
disc from #e7b921 to #d9b84a. Addresses review feedback on #180.
@cpsievert

Copy link
Copy Markdown
Collaborator Author

@simonpcouch feel free to merge away if c8d1ded looks good to you

@simonpcouch
simonpcouch merged commit a83c012 into main Aug 27, 2026
3 of 4 checks passed
@simonpcouch
simonpcouch deleted the feat/aside-icon-markers branch August 27, 2026 18:34
@github-actions

Copy link
Copy Markdown

Cleaned up 5 preview bundle(s) on https://dogfood.team.pct.posit.it: 365341, 365345, 365365, 365366, 365373

@github-actions

Copy link
Copy Markdown

Cleaned up 5 preview bundle(s) on https://connect.staging.pct.posit.it: 2279, 2284, 2295, 2296, 2301

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

consider switching to the default shinychat citation UI

2 participants